from config import config
import serial
import logging
import struct
import enum

logger = logging.getLogger()

class OS_CTRL:

    class OS_CMD(enum.IntEnum):
        SET_START_FREQ      = 0x93
        SET_SCAN_SETP       = 0x94
        SET_STOP_FREQ       = 0x95
        SET_OPEN            = 0x97
        SET_SCAN_MODE       = 0xA4
        SET_SCAN_SPEED      = 0x96

    class SCAN_MODE(enum.IntEnum):
        ONE_SHOT            = 0x01
        FOREVER             = 0x02
        STOP                = 0x00

    class SCAN_SPEED(enum.IntEnum):
        STEP_1US            = 0x01
        STEP_2US            = 0x02
        STEP_10US           = 0x03
        STEP_980US          = 0x04

    class CMD_TYPE(enum.IntEnum):
        RD = 0
        WR = 1

    def __init__(self, cfg:config) -> None:

        self.cfg = cfg
        
        self.os_uart = serial.Serial(self.cfg.os_uart, self.cfg.os_uart_baudrate, timeout=1)
        if self.os_uart.is_open:
            logger.info(f'Opt source UART is opened on {self.os_uart.name}.')
        else:
            logger.error('Opt source UART open FAILED.')
    
    
    def __bip4(self, num:int) -> int:
        packed = struct.pack('!I', num)
        integers = struct.unpack('!4B', packed)
        bip8 = (integers[0] & 0xF) ^ integers[1] ^ integers[2] ^ integers[3]
        bip4 = (bip8 >> 4) ^ (bip8 & 0xF)
        return bip4
    
    def __send_cmd(self, cmd, cmd_type, val):
        cmd_frame = (cmd_type << 24) | ((cmd & 0xFF) << 16) | ((val & 0xFFFF))
        bip4 = self.__bip4(cmd_frame)
        cmd_int = (bip4 << 28) | cmd_frame
        bstr = struct.pack('!I', cmd_int)

        send_n = self.os_uart.write(bstr)
        ret = self.os_uart.read(100)
        logger.info(f'OS send {bstr}')
        logger.info(f'OS reply {ret}')
        return 1

    
    def set_start_freq(self, freq=None):
        if freq is None:
            val = self.cfg.start_pos
        else:
            val = freq

        return self.__send_cmd(self.OS_CMD.SET_START_FREQ, self.CMD_TYPE.WR, val)

    
    def set_scan_step(self, step=None):
        if step is None:
            val = self.cfg.scan_step
        else:
            val = step

        return self.__send_cmd(self.OS_CMD.SET_SCAN_SETP, self.CMD_TYPE.WR, val)

    
    def set_stop_freq(self, freq=None):
        if freq is None:
            val = self.cfg.stop_pos
        else:
            val = freq

        return self.__send_cmd(self.OS_CMD.SET_STOP_FREQ, self.CMD_TYPE.WR, val)
    
    
    def set_scan_speed(self, speed_sel:SCAN_SPEED=None):
        if speed_sel is None:
            val = self.cfg.scan_speed
        else:
            val = speed_sel.value

        return self.__send_cmd(self.OS_CMD.SET_SCAN_SPEED, self.CMD_TYPE.WR, val)
    

    def set_open(self, on):
        val = 0xFFFF if on else 0x0

        return self.__send_cmd(self.OS_CMD.SET_OPEN, self.CMD_TYPE.WR, val)
    
    
    def set_scan_mode(self, mode:SCAN_MODE=SCAN_MODE.FOREVER):
        val = mode

        return self.__send_cmd(self.OS_CMD.SET_SCAN_MODE, self.CMD_TYPE.WR, val)

    
